package com.cdzs.twocamera.tcp;

import android.util.Log;

import com.cdzs.twocamera.constant.Constant;
import com.cdzs.twocamera.myinterface.OnReceiveCallbackBlock;
import com.cdzs.twocamera.myinterface.OnServerConnectedCallbackBlock;
import com.cdzs.twocamera.myinterface.OnServerDisconnectedCallbackBlock;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.charset.Charset;

/**
 * Created by M_genius on 2018/7/10.
 * 描述:
 */

public class TCPClient {

    private static final String TAG = TCPClient.class.getSimpleName();

    private static final int CONNECT_TIMEOUT = 10000;

    private Socket mSocket;
    private InputStream mInputStream;
    private OutputStream mOutputStream;

    private static volatile TCPClient mInstance;

    private TCPClient() {
    }

    /**
     * 单例获取 TCP 连接对象
     *
     * @return
     */
    public static TCPClient getInstance() {

        if (mInstance == null) {
            synchronized (TCPClient.class) {
                if (mInstance == null) {
                    mInstance = new TCPClient();
                }
            }
        }
        return mInstance;
    }

//==============================抛出相关接口，在外部处理=================================================
    /**
     * 连接回调
     */
    private OnServerConnectedCallbackBlock connectedCallback;

    /**
     * 断开连接回调(连接失败)
     */
    private OnServerDisconnectedCallbackBlock disconnectedCallback;

    /**
     * 接收信息回调
     */
    private OnReceiveCallbackBlock receivedCallback;


    public void setConnectedCallback(OnServerConnectedCallbackBlock connectedCallback) {
        this.connectedCallback = connectedCallback;
    }

    public void setDisconnectedCallback(OnServerDisconnectedCallbackBlock disconnectedCallback) {
        this.disconnectedCallback = disconnectedCallback;
    }

    public void setReceivedCallback(OnReceiveCallbackBlock receivedCallback) {
        this.receivedCallback = receivedCallback;
    }

//==============================抛出相关接口，在外部处理=================================================


    /**
     * 移除回调
     */
    private void removeCallback() {

        connectedCallback = null;
        disconnectedCallback = null;
        receivedCallback = null;
    }


//    /**
//     * 连接主机
//     *
//     * @param host 主机IP地址
//     * @param port 主机端口
//     */
//    public void connect(final String host, final int port) {
//
//        if (mSocket != null) {
//            close();
//        }
//
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                try {
//                    if (mSocket == null) {
//                        mSocket = new Socket();
//                        SocketAddress socketAddress = new InetSocketAddress(host, port);
//                        // 设置连接超时时间
//                        mSocket.connect(socketAddress, CONNECT_TIMEOUT);
//                    }
//
//                    if (mSocket.isConnected()) {
//                        logI("connected to host success");
//
//                        // 设置读流超时时间，必须在获取流之前设置
//                        mSocket.setSoTimeout(INPUT_STREAM_READ_TIMEOUT);
//
//                        mInputStream = mSocket.getInputStream();
//                        mOutputStream = mSocket.getOutputStream();
//
//                        new ReceiveThread().start();
//
//                    } else {
//                        mSocket.close();
//                    }
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            }
//        }).start();
//    }

    /**
     * 通过IP地址(域名)和端口进行连接
     */
    public void connect() {

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Log.d(TAG, "首次连接: IP地址" + Constant.SERVER_IP + "   端口号:" + Constant.SERVER_PORT);

                    mSocket = new Socket();

                    SocketAddress socketAddress = new InetSocketAddress(Constant.SERVER_IP, Constant.SERVER_PORT);

                    // 设置连接超时时间
                    mSocket.connect(socketAddress, CONNECT_TIMEOUT);


                    if (mSocket.isConnected()) {

                        // 连接成功回调
                        if (connectedCallback != null) {
                            connectedCallback.serverConnectedCallback();
                        }

                        mOutputStream = mSocket.getOutputStream();
                        mInputStream = mSocket.getInputStream();

                        //receive();

                        new ReceiveThread().start();

                        Log.i(TAG, "连接成功");

                    } else {
                        if (disconnectedCallback != null) {
                            disconnectedCallback.serverDisconnectedCallback(new IOException("连接失败"));
                        }
                    }
                } catch (IOException e) {

                    e.printStackTrace();

                    // 断开连接回调
                    if (disconnectedCallback != null) {
                        disconnectedCallback.serverDisconnectedCallback(e);
                    }
                }
            }
        });
        thread.start();
    }


    /**
     * 接收线程
     */
    class ReceiveThread extends Thread {

        @Override
        public void run() {
            super.run();

            while (mSocket != null && mSocket.isConnected() && mInputStream != null) {

                // 读取流
                byte[] data = new byte[0];
                byte[] buf = new byte[1024];
                int len;
                try {
                    while ((len = mInputStream.read(buf)) != -1) {
                        byte[] temp = new byte[data.length + len];
                        System.arraycopy(data, 0, temp, 0, data.length);
                        System.arraycopy(buf, 0, temp, data.length, len);
                        data = temp;
                    }
                } catch (IOException e) {
                    //e.printStackTrace();
                }

                // 处理流，在此处处理接收的字节数组
                if (data.length != 0) {
                    if (receivedCallback != null) {
                        receivedCallback.callback(data);
                    }
                }
            }
        }
    }

    /**
     * 发送数据
     *
     * @param data 数据
     */
    public void send(final byte[] data) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                if (mSocket != null) {
                    try {
                        mOutputStream.write(data);
                        mOutputStream.flush();
                        Log.i(TAG, "发送成功");
                    } catch (IOException e) {
                        e.printStackTrace();
                        Log.i(TAG, "发送失败");
                    }
                } else {
                    Log.d(TAG, "发送连接: IP地址" + Constant.SERVER_IP + "   端口号:" + Constant.SERVER_PORT);
                    connect();
                }
            }
        }).start();
    }

    /**
     * 发送数据
     */
    public void sendEasy(byte[] data) {

        if (mSocket != null && mSocket.isConnected() && mOutputStream != null) {
            try {
                mOutputStream.write(data);
                mOutputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 接收数据
     */
    public void receive() {

        while (mSocket.isConnected()) {
            try {
                /**得到的是16 进制数，需要进行解析*/
                byte[] bt = new byte[1024];
                //获取接收到的字节和字节数
                int length = mInputStream.read(bt);
                //获取正确的字节
                byte[] bs = new byte[length];

                System.arraycopy(bt, 0, bs, 0, length);

                String str = new String(bs, "UTF-8");
                if (str != null) {
                    if (receivedCallback != null) {
                        receivedCallback.callback(bt);
                    }
                }
                Log.i(TAG, "接收成功");
            } catch (IOException e) {
                Log.i(TAG, "接收失败");
            }
        }
    }

    /**
     * 发送字符串 UTF-8 编码字节
     *
     * @param data
     */
    public void send(String data) {
        send(data.getBytes(Charset.forName("UTF-8")));
    }

    /**
     * 关闭连接
     */
    public void close() {
        if (mSocket != null) {
            try {
                mInputStream.close();
                mOutputStream.close();
                mSocket.close();
                mInputStream = null;
                mOutputStream = null;
                mSocket = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 断开连接
     */
    public void disconnect() {
        if (mSocket.isConnected()) {
            try {
                if (mOutputStream != null) {
                    mOutputStream.close();
                    mOutputStream = null;
                }
                if (mInputStream != null) {
                    mInputStream.close();
                    mInputStream = null;
                }
                if (mSocket != null) {
                    mSocket.close();
                }
                if (mSocket.isClosed()) {
                    if (disconnectedCallback != null) {
                        disconnectedCallback.serverDisconnectedCallback(new IOException("断开连接"));
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    public Socket getSocket() {
        return mSocket;
    }

    private void logI(String msg) {
        Log.i(TAG, msg);
    }
}
